#include "game.h"
#include <stdbool.h>
#include <stdio.h>
#include "util.h"
#include "snake.h"
#include "snakeio.h"
#include "SDL3/SDL.h"
#include "SDL3/SDL_render.h"
#include <SDL3_ttf/SDL_ttf.h>
#include <winsock2.h>
#include <ws2tcpip.h>

//配置
extern int MAX_PLAYERS;
extern int GRID_SIZE;
extern int CELL_SIZE;
extern int DELTA_TIME;
extern int MOVE_INTERVAL;
extern int WINDOW_WIDTH;
extern int WINDOW_HEIGHT;
extern int SNAKE_CAPACITY;
extern char playerName[17];
extern bool running;
extern bool inited;
extern bool syncing;
extern Food food;
extern Player *localPlayer;
extern SOCKET sockFD;
extern PacketRingBuffer rb;
extern HANDLE netIOThread;
extern int cTick;
extern int sTick;
extern Packet *frameBuffer[1024];
extern const int delayFrames;
extern int score;
extern int playerCount;
extern char localPlayerId[64];
extern volatile bool socketThreadRunning;
extern int tickCounter;
extern int syncCounter;

//State
// 配置参数
int MAX_PLAYERS;
int GRID_SIZE;
int CELL_SIZE;
int DELTA_TIME;
int MOVE_INTERVAL;
int GAME_WIDTH;
int GAME_HEIGHT;
int SIDEBAR_WIDTH = 200;
int WINDOW_WIDTH;
int WINDOW_HEIGHT;
int SNAKE_CAPACITY;

char playerName[17] = {};

// 游戏状态
bool running = true;
bool inited = false;
bool syncing = false;

Food food;
Player *players = NULL;
Player *localPlayer;

SOCKET sockFD;
PacketRingBuffer rb;
HANDLE netIOThread;

int cTick = 0;
int sTick = 0;
int tickCounter = 0;
int syncCounter = 0;

Packet *frameBuffer[1024] = {0};
const int delayFrames = 1;
int score = 0;
int playerCount = 0;

char localPlayerId[64] = "";
volatile bool socketThreadRunning = true;

// 渲染相关
TTF_Font *font;
SDL_Texture *sidebarTexture = NULL;
bool sidebarDirty = true;

extern SDL_Window *window;
extern SDL_Renderer *renderer;

int init_net(const char *host, const int port) {
    const SOCKET socket = initClientSocket(host, port);
    if (socket == INVALID_SOCKET) {
        return -1;
    }
    sockFD = socket;
    return 1;
}

bool framebuffer_store(Packet *pkt) {
    if (!pkt) return false;
    const int frame = pkt->frame.index;
    const int idx = frame % 1024;
    // 如果已有包，先释放（避免内存泄漏）
    if (frameBuffer[idx] != NULL) {
        snake_free_packet(frameBuffer[idx]);
        frameBuffer[idx] = NULL;
    }
    frameBuffer[idx] = pkt;
    return true;
}

Packet *framebuffer_get(const int frame) {
    const int idx = frame % 1024;
    return frameBuffer[idx];
}

void framebuffer_clear(const int frame) {
    const int idx = frame % 1024;
    if (frameBuffer[idx]) {
        snake_free_packet(frameBuffer[idx]);
        frameBuffer[idx] = NULL;
    }
}

//玩家
//查找玩家
Player *find_player(const char *playerId) {
    Player *p = NULL;
    HASH_FIND_STR(players, playerId, p);
    return p; // NULL if not found
}

//移除玩家
void remove_player(const char *playerId) {
    Player *p;
    HASH_FIND_STR(players, playerId, p);
    if (p) {
        if (p->snake.body) free(p->snake.body);
        HASH_DEL(players, p);
        free(p);
    }
}

//清理所有玩家
void cleanup_players() {
    Player *p, *tmp;
    HASH_ITER(hh, players, p, tmp) {
        if (p->snake.body) {
            free(p->snake.body);
            p->snake.body = NULL;
        }
        HASH_DEL(players, p);
        free(p);
    }
    players = NULL;
}

//添加玩家
Player *add_player_if_not_exists(Player *new_p) {
    Player *p;
    HASH_FIND_STR(players, new_p->playerId, p);
    if (p == NULL) {
        Player *player = malloc(sizeof(Player));
        strCopy(new_p->playerId,player->playerId, sizeof(player->playerId));
        strncpy(player->playerName, new_p->playerName, sizeof(player->playerName) - 1);
        player->snake.body = malloc(SNAKE_CAPACITY * sizeof(Point));
        player->snake.length = new_p->snake.length;
        player->snake.capacity = SNAKE_CAPACITY;
        player->snake.direction = new_p->snake.direction;
        player->snake.next_direction = new_p->snake.direction;
        for (int i = 0; i < new_p->snake.length; i++) {
            player->snake.body[i].x = new_p->snake.body[i].x;
            player->snake.body[i].y = new_p->snake.body[i].y;
        }
        HASH_ADD_STR(players, playerId, player);
    }
    return find_player(new_p->playerId);
}

//数据包操作
Packet *read_and_parse_packet() {
    // 先读取包长度（4字节网络序）
    uint32_t len_net = 0;
    int ret = recv(sockFD, (char *) &len_net, sizeof(len_net), MSG_WAITALL);
    if (ret <= 0) return NULL;
    uint32_t len = ntohl(len_net);
    if (len == 0 || len > 65536) return NULL;
    // 分配缓存接收原始数据
    char *buf = malloc(len + 1);
    if (!buf) return NULL;
    ret = recv(sockFD, buf, len, MSG_WAITALL);
    if (ret <= 0) {
        free(buf);
        return NULL;
    }
    buf[len] = '\0'; // 确保是字符串（如果json是字符串）
    Packet *pkt = stringToPacket(buf);
    free(buf);
    return pkt; // 返回已经解析好的packet
}

//拉取配置
void pullServerConfig(const PacketInit *initPkt) {
    if (inited) {
        return;
    }
    GRID_SIZE = initPkt->gridSize;
    CELL_SIZE = initPkt->cellSize;
    MAX_PLAYERS = initPkt->maxPlayers;
    DELTA_TIME = initPkt->delay;
    cTick = initPkt->index;
    MOVE_INTERVAL = initPkt->moveInterval;
    GAME_HEIGHT = GRID_SIZE * CELL_SIZE;
    GAME_WIDTH = GRID_SIZE * CELL_SIZE;
    WINDOW_HEIGHT = GAME_HEIGHT;
    WINDOW_WIDTH = GAME_WIDTH + SIDEBAR_WIDTH;
    SNAKE_CAPACITY = GRID_SIZE * GRID_SIZE;
    localPlayer = add_player_if_not_exists(initPkt->me);
    strncpy(localPlayerId, localPlayer->playerId, sizeof(localPlayer->playerId) - 1);
    strncpy(playerName, localPlayer->playerName, sizeof(playerName) - 1);
    inited = true;
}

//同步状态
void syncServerState(const PacketSync *sync) {
    playerCount = sync->playersCount;
    for (int i = 0; i < playerCount; i++) {
        Player *pSync = sync->players[i];
        if (!pSync) continue;
        Player *p = find_player(pSync->playerId);
        if (!p) {
            Player *tmp = find_player(pSync->playerId);
            if (!tmp) {
                Player *new_p = malloc(sizeof(Player));
                strcpy(new_p->playerId, pSync->playerId);
                strcpy(new_p->playerName, pSync->playerName);
                new_p->snake.length = pSync->snake.length;
                new_p->snake.direction = pSync->snake.direction;
                new_p->snake.next_direction = pSync->snake.direction;
                new_p->snake.capacity = SNAKE_CAPACITY;
                new_p->snake.body = malloc(new_p->snake.capacity * sizeof(Point));
                for (int j = 0; j < new_p->snake.length; j++) {
                    new_p->snake.body[j].x = pSync->snake.body[j].x;
                    new_p->snake.body[j].y = pSync->snake.body[j].y;
                }
                add_player_if_not_exists(new_p);
            }
        } else {
            p->snake.length = pSync->snake.length;
            p->snake.direction = pSync->snake.direction;
            for (int j = 0; j < p->snake.length; j++) {
                p->snake.body[j].x = pSync->snake.body[j].x;
                p->snake.body[j].y = pSync->snake.body[j].y;
            }
        }
    }
    food.position.x = sync->nowFood.position.x;
    food.position.y = sync->nowFood.position.y;
    cTick = sync->index;
    tickCounter = sync->tickCounter;
    syncCounter++;
    sidebarDirty = true;
    syncing = false;
}

//处理非帧包
void processNonFramePacket(Packet *pkt) {
    if (!pkt) return;
    switch (pkt->type) {
        case PACKET_TYPE_INIT: {
            pullServerConfig(&pkt->init);
            break;
        }

        case PACKET_TYPE_SYNC: {
            syncing = true;
            syncServerState(&pkt->sync);
            break;
        }

        default:
            break;
    }
}

//线程
DWORD WINAPI socket_thread_func(LPVOID param) {
    while (socketThreadRunning) {
        Packet *pkt = read_and_parse_packet();
        if (!pkt) {
            continue;
        }
        if (pkt->type == PACKET_TYPE_SYNC) {
            syncing = true;
        }
        if (pkt->type == PACKET_TYPE_FRAME) {
        }
        if (!ringbuffer_push(&rb, pkt)) {
            printf("缓冲区满\n");
            snake_free_packet(pkt);
        }
    }
    return 0;
}

HANDLE start_socket_thread() {
    const HANDLE t = CreateThread(
        NULL,
        0,
        socket_thread_func,
        NULL,
        0,
        NULL);
    if (t == NULL) {
        printf("CreateThread失败，错误码: %lu\n", GetLastError());
    }
    return t;
}

int pre_init(const char *host, const int port) {
    initPacketRingbuffer(&rb);
    init_net(host, port);
    netIOThread = start_socket_thread();
    if (netIOThread == NULL) {
        return 1;
    }
    sendPacketByType(PACKET_TYPE_INIT);
    return 0;
}

void init() {
    sendPacketByType(PACKET_TYPE_SYNC);
    TTF_Init();
    font = TTF_OpenFont("C:\\Windows\\Fonts\\msyh.ttc", 24);
    if (!font) {
        SDL_Log("Failed to load font: %s", SDL_GetError());
    }
}

void gameOver() {
    sendPacketByType(PACKET_TYPE_ESC);
    running = false;
}

bool isSnakeOutOfBounds(const Snake *s) {
    return s->body[0].x < 0 || s->body[0].x >= GRID_SIZE || s->body[0].y < 0 || s->body[0].y >= GRID_SIZE;
}

void moveSnake(Snake *snake, const Direction dir) {
    if (!snake || snake->length <= 0) return;
    // 从尾到头移动身体
    for (int i = snake->length - 1; i > 0; i--) {
        snake->body[i] = snake->body[i - 1];
    }
    // 更新蛇头位置
    Point *head = &snake->body[0];
    switch (dir) {
        case DIR_UP: head->y -= 1;
            break;
        case DIR_DOWN: head->y += 1;
            break;
        case DIR_LEFT: head->x -= 1;
            break;
        case DIR_RIGHT: head->x += 1;
            break;
        default: break;
    }
}


// 更新程蛇方向（把next_direction赋值给direction）
void updateSnakeDirections() {
    Player *p, *tmp;
    HASH_ITER(hh, players, p, tmp) {
        if (p->snake.direction != p->snake.next_direction) {
            p->snake.direction = p->snake.next_direction;
        }
    }
}

void cleanup() {
    socketThreadRunning = false;
    if (netIOThread) {
        WaitForSingleObject(netIOThread, 1000);
    }
    // 释放players中所有玩家的蛇身体和玩家对象（包括本地和远程）
    Player *p, *tmp;
    HASH_ITER(hh, players, p, tmp) {
        if (p->snake.body) {
            free(p->snake.body);
            p->snake.body = NULL;
        }
        HASH_DEL(players, p);
        free(p);
    }
    if (renderer) {
        SDL_DestroyRenderer(renderer);
        renderer = NULL;
    }
    if (window) {
        SDL_DestroyWindow(window);
        window = NULL;
    }
    SDL_Quit();
    closeClientSocket(sockFD);
    if (netIOThread) {
        TerminateThread(netIOThread, 0); // 不推荐，但保留
        CloseHandle(netIOThread);
        netIOThread = NULL;
    }
    Packet *pkt = NULL;
    while ((pkt = ringbuffer_pop(&rb)) != NULL) {
        snake_free_packet(pkt);
    }
}

void updateWindowTitle() {
    char title[64];
    snprintf(title, sizeof(title), "Snake4c - Players: %d", playerCount);
    SDL_SetWindowTitle(window, title);
}

Direction sdDirMap(const SDL_Keycode key) {
    switch (key) {
        case SDLK_UP: return DIR_UP;
        case SDLK_DOWN: return DIR_DOWN;
        case SDLK_LEFT: return DIR_LEFT;
        case SDLK_RIGHT: return DIR_RIGHT;
        default: return DIR_NONE;
    }
}

void handleInput() {
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        if (event.type == SDL_EVENT_QUIT) {
            gameOver();
            break;
        }
        if (event.type == SDL_EVENT_KEY_DOWN) {
            switch (event.key.key) {
                case SDLK_UP:
                    if (localPlayer->snake.direction != DIR_DOWN) {
                        sendDirPacket(DIR_UP);
                    }
                    break;
                case SDLK_DOWN:
                    if (localPlayer->snake.direction != DIR_UP) {
                        sendDirPacket(DIR_DOWN);
                    }
                    break;
                case SDLK_LEFT:
                    if (localPlayer->snake.direction != DIR_RIGHT) {
                        sendDirPacket(DIR_LEFT);
                    }
                    break;
                case SDLK_RIGHT:
                    if (localPlayer->snake.direction != DIR_LEFT) {
                        sendDirPacket(DIR_RIGHT);
                    }
                    break;
                case SDLK_ESCAPE: {
                    sendPacketByType(PACKET_TYPE_ESC);
                    gameOver();
                }
                break;
                default:
                    break;
            }
        }
    }
}


void updateSidebarTexture(SDL_Renderer *renderer, TTF_Font *font) {
    if (!sidebarDirty) return;
    if (sidebarTexture) {
        SDL_DestroyTexture(sidebarTexture);
        sidebarTexture = NULL;
    }
    // 创建一个目标纹理用于绘制 UI 内容
    sidebarTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888,
                                       SDL_TEXTUREACCESS_TARGET,
                                       SIDEBAR_WIDTH, WINDOW_HEIGHT);
    SDL_SetRenderTarget(renderer, sidebarTexture);
    SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255);
    SDL_RenderClear(renderer);
    int yOffset = 20;
    Player *p, *tmp;
    HASH_ITER(hh, players, p, tmp) {
        char scoreText[128];
        snprintf(scoreText, sizeof(scoreText), "%s: %d", p->playerName, p->snake.length);
        SDL_Surface *surface = TTF_RenderText_Blended(font, scoreText, strlen(scoreText),
                                                      (SDL_Color){255, 255, 255, 255});
        SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface);
        SDL_FRect textRect = {10, yOffset, surface->w, surface->h};
        SDL_RenderTexture(renderer, texture, NULL, &textRect);
        SDL_DestroyTexture(texture);
        yOffset += 30;
    }
    SDL_SetRenderTarget(renderer, NULL); // 切回主渲染目标
    sidebarDirty = false;
}

void render(float alpha) {
    updateSidebarTexture(renderer, font);
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer);

    SDL_Rect gameArea = {0, 0, GAME_WIDTH, GAME_HEIGHT};
    SDL_SetRenderClipRect(renderer, &gameArea);

    // 渲染蛇，遍历哈希表players
    Player *p, *tmp;
    HASH_ITER(hh, players, p, tmp) {
        SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255);
        if (strcmp(p->playerId, localPlayerId) == 0) {
            SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
        }
        for (int j = 0; j < p->snake.length; j++) {
            SDL_FRect rect = {
                p->snake.body[j].x * CELL_SIZE,
                p->snake.body[j].y * CELL_SIZE,
                CELL_SIZE,
                CELL_SIZE
            };
            SDL_RenderFillRect(renderer, &rect);
        }
    }
    // 渲染食物
    SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
    const SDL_FRect rect = {
        food.position.x * CELL_SIZE,
        food.position.y * CELL_SIZE,
        CELL_SIZE,
        CELL_SIZE
    };
    SDL_RenderFillRect(renderer, &rect);


    SDL_SetRenderClipRect(renderer, NULL); // 取消剪裁，准备渲染 UI
    if (sidebarTexture) {
        const SDL_FRect dst = {GAME_WIDTH, 0, SIDEBAR_WIDTH, WINDOW_HEIGHT};
        SDL_RenderTexture(renderer, sidebarTexture, NULL, &dst);
    }

    updateWindowTitle();
    SDL_RenderPresent(renderer);
}

void applyFrameItem(const PacketFrameItem *pkt) {
    Player *p = find_player(pkt->playerId);
    if (!p) return;
    p->snake.next_direction = pkt->direction;
}

int serverTick() {
    Packet *pkt = ringbuffer_pop(&rb);
    if (pkt == NULL) return -1;
    if (!is_frame_packet(pkt)) {
        processNonFramePacket(pkt);
        snake_free_packet(pkt);
        return -1;
    }
    // 是帧包，存到帧缓存
    const int frame = pkt->frame.index;
    framebuffer_store(pkt);
    return frame;
}

// 移动所有远程蛇
void moveSnakes() {
    updateSnakeDirections();
    Player *p, *tmp;
    HASH_ITER(hh, players, p, tmp) {
        moveSnake(&p->snake, p->snake.direction);
    }
}

void updateLogic(Packet *pkt) {
    if (!pkt) return;
    const int frameCount = pkt->frame.frameCount;
    const PacketFrameItem *frames = pkt->frame.frames;
    for (int i = 0; i < frameCount; i++) {
        const PacketFrameItem *item = &frames[i];
        switch (item->type) {
            case PACKET_TYPE_DIR: {
                const Player *p = find_player(item->playerId);
                if (p) applyFrameItem(item);
                break;
            }
            case PACKET_TYPE_MOVE: {
                break;
            }
            case PACKET_TYPE_FOOD_CREATE: {
                food.position = item->point;
                break;
            }
            case PACKET_TYPE_FOOD_EATEN: {
                Player *p = find_player(item->playerId);
                if (p) {
                    p->snake.length++;
                    sidebarDirty = true;
                }
                break;
            }
            case PACKET_TYPE_EXIT: {
                const Player *p = find_player(item->playerId);
                if (p) {
                    remove_player(p->playerId);
                    playerCount--;
                    sidebarDirty = true;
                }
                break;
            }
            case PACKET_TYPE_JOIN: {
                if (strcmp(item->playerId, localPlayer->playerId) != 0) {
                    Player *p = find_player(item->playerId);
                    if (!p) {
                        Player *new_p = malloc(sizeof(Player));
                        strCopy(item->playerId, new_p->playerId, sizeof(new_p->playerId));
                        strCopy(item->playerName, new_p->playerName, sizeof(new_p->playerName));
                        new_p->snake.length = item->snake.length;
                        new_p->snake.direction = item->snake.direction;
                        new_p->snake.next_direction = item->snake.direction;
                        new_p->snake.capacity = SNAKE_CAPACITY;
                        new_p->snake.body = malloc(SNAKE_CAPACITY * sizeof(Point));
                        for (int j = 0; j < new_p->snake.length; j++) {
                            new_p->snake.body[j].x = item->snake.body[j].x;
                            new_p->snake.body[j].y = item->snake.body[j].y;
                        }
                        add_player_if_not_exists(new_p);
                        playerCount++;
                        sidebarDirty = true;
                        free_player(new_p);
                    }
                }
                break;
            }
            default:
                break;
        }
    }
    if ((cTick % MOVE_INTERVAL) == 0) {
        moveSnakes();
    }
}
